<?php
// +---------------------------------------------------------------------+
// | NiuCloud | [ WE CAN DO IT JUST NiuCloud ]                |
// +---------------------------------------------------------------------+
// | Copy right 2019-2029 www.niucloud.com                          |
// +---------------------------------------------------------------------+
// | Author | NiuCloud <niucloud@outlook.com>                       |
// +---------------------------------------------------------------------+
// | Repository | https://github.com/niucloud/framework.git          |
// +---------------------------------------------------------------------+

namespace addon\wxoplatform\model;


use app\model\upload\Upload;
use EasyWeChat\Factory;
use app\model\BaseModel;
use EasyWeChat\OpenPlatform\Auth\VerifyTicket;
use think\facade\Cache;
use EasyWeChat\Kernel\Traits\InteractsWithCache;
use addon\weapp\model\Config as WeappConfig;

/**
 * 微信开放平台(为调用easywehcat)
 * 版本 1.0.4
 */
class OpenPlatform extends BaseModel
{
    use InteractsWithCache;

    private $app;//微信公众对象


    /**
     * 公众号的AppID
     * @var string
     */
    public $appid = "";

    /**
     * 公众号密钥
     * @var string
     */
    protected $appsecret = "";

    /**
     * 微信accessToken
     * @var string
     */
    public $accessToken = "";

    /**
     * 微信公众平台刷新token
     * @var string
     */
    public $refreshToken;

    /**
     * 微信开放平台appid
     * @var string
     */
    public $platformAppid;

    /**
     * 微信开放平台appsecret
     * @var string
     */
    public $platformAppsecret;

    /**
     * 微信开放平台加密秘钥
     * @var string
     */
    public $platformEncodingaeskey;

    /**
     * 微信开放平台token
     * @var string
     */
    public $platformToken;


    /**
     * 微信开放平台componentAccessToken
     * @var string
     */
    public $componentAccessToken;

    /**
     * 微信开放平台ticket
     * @var string
     */
    public $ticket;

    /**
     * 微信开放平台预授权码
     * @var string
     */
    public $preAuthCode;

    /**
     * 错误码
     * @var array
     */
    private $error = [
        85013 => '无效的自定义配置',
        85014 => '无效的模版编号',
        85043 => '模版错误',
        85044 => '代码包超过大小限制',
        85045 => 'ext_json有不存在的路径',
        85046 => 'tabBar中缺少 path',
        85047 => 'pages字段为空',
        85048 => 'ext_json解析失败',
        80082 => '没有权限使用该插件',
        80067 => '找不到使用的插件',
        80066 => '非法的插件版本',
        86000 => '不是由第三方代小程序进行调用',
        86001 => '不存在第三方的已经提交的代码',
        85006 => '标签格式错误',
        85007 => '页面路径错误',
        85008 => '类目填写错误',
        85009 => '已经有正在审核的版本',
        85077 => '小程序类目信息失效（类目中含有官方下架的类目，请重新选择类目）',
        86002 => '小程序还未设置昵称、头像、简介。请先设置完后再重新提交',
        85085 => '小程序提审数量已达本月上限，请点击查看《临时quota申请流程》',
        85086 => '提交代码审核之前需提前上传代码',
        85087 => '小程序已使用 api navigateToMiniProgram，请声明跳转 appid 列表后再次提交',
        87006 => '小游戏不能提交',
        86007 => '小程序禁止提交',
        87013 => '撤回次数达到上限（每天一次，每个月 10 次）',
        85019 => '没有审核版本',
        85020 => '审核状态未满足发布'
    ];

    public function __construct($site_id = 0)
    {
        // 三方平台配置
        $config_model = new Config();
        $config_result = $config_model->getOplatformConfig();
        $config = $config_result["data"];

        if (!empty($config)) {
            $config_info = $config["value"];
        }

        $config = [
            'app_id' => $config_info["appid"] ?? '',
            'secret' => $config_info["secret"] ?? '',
            'token' => $config_info["token"] ?? '',
            'aes_key' => $config_info["aes_key"] ?? '',

            'log' => [
                'level' => 'debug',
                'permission' => 0777,
                'file' => 'runtime/log/wechat/easywechat.logs',
            ],
        ];
        $this->app = $this->initWechatPlatformAccount($config);

        // 获取授权小程序配置
        $weapp_config_model = new WeappConfig();
        $weapp_config = $weapp_config_model->getWeappConfig($site_id);
        $weapp_config = $weapp_config['data']['value'];

        $this->appid = $weapp_config['authorizer_appid'] ?? '';
        $this->refreshToken = $weapp_config['authorizer_refresh_token'] ?? '';

        //
//        $response = $this->app->server->serve();
        // 将响应输出
//        $response->send();exit; // Laravel 里请使用：return $response;
    }

    /**
     * 初始化开放平台账户
     * @param string $config
     */
    public function initWechatPlatformAccount($config = [])
    {

        $this->platformAppid = $config['app_id'];
        $this->platformAppsecret = $config['secret'];
        $this->platformEncodingaeskey = $config['aes_key'];
        $this->platformToken = $config['token'];

        $this->ticket = $this->getCache()->get('easywechat.open_platform.verify_ticket.'.$this->platformAppid);
        if (Cache::get('component_access_token') == false) {
            $this->getCommonAccessToken();
        } else {
            $this->componentAccessToken = Cache::get('component_access_token');
        }
        if (Cache::get('pre_auth_code') == false) {
            $this->getPreAuthCode();
        } else {
            $this->preAuthCode = Cache::get('pre_auth_code');
        }

    }


    /**
     * 获取第三方token,需要第三方的appid，密码，ticket     $component_token
     */
    private function getCommonAccessToken()
    {
        $url = "https://api.weixin.qq.com/cgi-bin/component/api_component_token";
        $data = array('component_appid' => $this->platformAppid, 'component_appsecret' => $this->platformAppsecret, 'component_verify_ticket' => $this->ticket);

        $result = $this->apiPost($url, $data);
        if (!empty($result['component_access_token'])) {
            Cache::set('component_access_token', $result['component_access_token'], 5400);
            $this->componentAccessToken = Cache::get('component_access_token');
        }
    }

    /**
     * 获取第三方平台的预授权码    需要第三方token，以及第三方appid
     */
    private function getPreAuthCode()
    {
        $url = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=" . $this->componentAccessToken;
        $data = array('component_appid' => $this->platformAppid);

        $result = $this->apiPost($url, $data);
        if (!empty($result['pre_auth_code'])) {
            Cache::set('pre_auth_code', $result['pre_auth_code'], 500);
            $this->preAuthCode = Cache::get('pre_auth_code');
        }
    }

    /**
     * 微信或小程序  网页授权后请求
     * @param $url
     * @param $param
     * @param bool $needToken
     * @return array|mixed
     */
    public function weappRequest($url, $param, $needToken = false, $method = 'post')
    {
        // 第一次为空，则从文件中读取
        if (empty($this->accessToken)) {
            $this->accessToken = Cache::get('accessToken-' . $this->appid);
        }
        // 为空则重新取值
        if (empty($this->accessToken) or $needToken) {
            //开放平台网页授权
            $this->getAccessTokenByWechatPlatform();
            $this->accessToken = Cache::get('accessToken-' . $this->appid);
        }

        $new_url = sprintf($url, $this->accessToken);
        $result = $this->apiPost($new_url, $param, $method);
        if (!empty($result['errcode'])) {
            switch ($result['errcode']) {
                case 40001:
                    return $this->weappRequest($url, $param, true); // 获取access_token时AppSecret错误，或者access_token无效
                    break;
                case 40014:
                    return $this->weappRequest($url, $param, true); // 不合法的access_token
                    break;
                case 42001:
                    return $this->weappRequest($url, $param, true); // access_token超时
                    break;
                case 45009:
                    return $this->error([], "接口调用超过限制：" . $result['errmsg']);
                    break;
                case 41001:
                    return $this->error([], "缺少access_token参数：" . $result['errmsg']);
                    break;
                default:
                    return $this->error($result, $this->error[ abs($result['errcode']) ] ?? $result['errmsg']); // 其他错误，抛出
                    break;
            }
        } else {
            return $this->success($result);
        }
    }

    /**
     * 通过上述方法获取的公众号access_token可能会过期，因此需要定时获取access_token
     */
    public function getAccessTokenByWechatPlatform()
    {
        //获取公众号token
        $url = "https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=" . $this->componentAccessToken;
        $data = array(
            'component_appid' => $this->platformAppid,
            'authorizer_appid' => $this->appid,
            'authorizer_refresh_token' => $this->refreshToken
        );
        $result = $this->apiPost($url, $data);
        if ($result == false || empty($result)) {
            return $this->error();
        } else {
            $token = $result['authorizer_access_token'] ?? '';
            if(!empty($token)){
                Cache::set('accessToken-' . $this->appid, $token, 3600);
                $this->accessToken = $token;
            }
            return $this->success();
        }
    }

    /**
     * 远程接口调用
     * @param $url
     * @param array $data
     * @return mixed
     */
    public function apiPost($url, $data = [], $method = 'post')
    {
        $curl = curl_init();  //创建一个新url资源
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        if ($method == 'post') {
            $data = json_encode($data, JSON_UNESCAPED_UNICODE);
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $ajax_return = curl_exec($curl);
        $code_arr = is_json($ajax_return) ? json_decode($ajax_return, true) : $ajax_return;
        return $code_arr;
    }

    /**
     * 获取代码模板列表
     * @param array $param
     */
    public function getTemplateList($param = [])
    {
        $url = "https://api.weixin.qq.com/wxa/gettemplatelist?access_token=" . $this->componentAccessToken;
        $data = array();
        $result = $this->apiPost($url, $data);
        if (isset($result['errcode']) && $result['errcode'] != 0) {
            return $this->error($result, $result["errmsg"]);
        }
        return $this->success($result['template_list']);
    }

    /**
     * 删除模板
     * @param array $param
     * @return array
     */
    public function deleteTemplate($param = []){
        $url = "https://api.weixin.qq.com/wxa/deletetemplate?access_token=" . $this->componentAccessToken;
        $data = array(
            'template_id' => $param['template_id']
        );
        $result = $this->apiPost($url, $data);
        if (isset($result['errcode']) && $result['errcode'] != 0) {
            return $this->error($result, $result["errmsg"]);
        }
        return $this->success($result);
    }

    /**
     * 获取开放平台代码模板列表
     * @param array $param
     */
    public function getDraftList($param = [])
    {
        $url = "https://api.weixin.qq.com/wxa/gettemplatedraftlist?access_token=" . $this->componentAccessToken;
        $data = array();
        $result = $this->apiPost($url, $data);
        if (isset($result['errcode']) && $result['errcode'] != 0) {
            return $this->error($result, $result["errmsg"]);
        }
        return $this->success($result['draft_list']);
    }

    /**
     *  将草稿添加到代码模板库
     * @param array $param
     * @return array
     */
    public function addToTemplate($param = []){
        $url = "https://api.weixin.qq.com/wxa/addtotemplate?access_token=" . $this->componentAccessToken;
        $data = array(
            'draft_id' => $param['draft_id']
        );
        $result = $this->apiPost($url, $data);
        if (isset($result['errcode']) && $result['errcode'] != 0) {
            return $this->error($result, $result["errmsg"]);
        }
        return $this->success($result);
    }

    /**
     * 小程序代码上传
     * 第三方平台需要先将草稿添加到代码模板库，或者中代码模板库中选取某个代码模板，得到对应的模板 id（template_id）； 然后调用本接口可以为已授权的小程序上传代码。
     * @param array $param
     * @return mixed
     */
    public function weappCommit($param = [])
    {
        $url = "https://api.weixin.qq.com/wxa/commit?access_token=%s";
        $ext_json = $param['ext_json'] ?? [];
        $data = array(
            'template_id' => $param['template_id'] ?? '',
            'ext_json' => json_encode($ext_json, JSON_UNESCAPED_UNICODE),
            'user_version' => $param['user_version'] ?? '',
            'user_desc' => $param['user_desc'] ?? '',
        );
        $result = $this->weappRequest($url, $data);
        if ($result['code'] < 0) {
            // 如果错误原因是“没有权限使用该插件”则去掉插件重新上传
            if (isset($result['data']['errcode']) && $result['data']['errcode'] == 80082) {
                $ext_json['plugins'] = (object)array();
                $data['ext_json'] = json_encode($ext_json, JSON_UNESCAPED_UNICODE);
                $result = $this->weappRequest($url, $data);
                if($result['code'] < 0)
                    return $result;
            } else {
                return $this->error([], $result['data']["errmsg"]);
            }
        }

        // 小程序代码上传之后提交审核
        $audit_result = $this->weappSubmitAudit();
        if($audit_result['code'] < 0)
            return $audit_result;

        if (isset($audit_result['data']['errcode']) && $audit_result['data']['errcode'] != 0) {
            return $this->error($audit_result, $audit_result['data']["errmsg"]);
        } else {
            $audit_data = [
                'site_id' => $ext_json['ext']['siteId'],
                'auditid' => $audit_result['data']['auditid'],
                'version' => $param['user_version'],
                'status' => 0,
                'create_time' => time(),
            ];
            $res = model('weapp_audit_record')->add($audit_data);
            return $this->success($res);
        }
    }

    /**
     * 小程序代码提交审核
     * 在调用上传代码接口为小程序上传代码后，可以调用本接口，将上传的代码提交审核。
     * @param array $param
     */
    public function weappSubmitAudit($param = []){
        $url = "https://api.weixin.qq.com/wxa/submit_audit?access_token=%s";
        $data = array(
            'item_list' => $param['item_list'] ?? [],//	审核项列表（选填，至多填写 5 项）
            'preview_info' => $param['preview_info'] ?? [],//预览信息（小程序页面截图和操作录屏）
            'version_desc' => $param['version_desc'] ?? '',//小程序版本说明和功能解释
            'feedback_info' => $param['feedback_info'] ?? '',//反馈内容，至多 200 字
            'feedback_stuff' => $param['feedback_stuff'] ?? ''//用 | 分割的 media_id 列表，至多 5 张图片, 可以通过新增临时素材接口上传而得到
        );

        $result = $this->weappRequest($url, $data);
        if($result['code'] < 0)
            return $result;

        if (isset($result['data']['errcode']) && $result['data']['errcode'] != 0) {
            return $this->error($result, $result['data']["errmsg"]);
        }
        return $result;
    }

    /**
     * 获取体验版二维码
     * @param array $param
     */
    public function getQrcode($param = []){
        $url = "https://api.weixin.qq.com/wxa/get_qrcode?access_token=%s";
        $data = [];
        $result = $this->weappRequest($url, $data, false, 'get');
        if($result['code'] < 0)
            return $result;

        if (isset($result['data']['errcode']) && $result['data']['errcode'] != 0) {
            return $this->error($result, $result['data']["errmsg"]);
        }

        $upload = new Upload();
        $result = $upload->setPath('qrcode/platform/')->remotePullBinary($result['data']);
        return $result;
    }

    /**
     * 查询最新一次提交的审核状态
     * @param array $param
     * @return array|mixed
     */
    public function getLatestAuditstatus($param = []){
        $url = "https://api.weixin.qq.com/wxa/get_latest_auditstatus?access_token=%s";
        $data = array(
        );
        $result = $this->weappRequest($url, $data, false, 'get');
        if($result['code'] < 0)
            return $result;

        if (isset($result['data']['errcode']) && $result['data']['errcode'] != 0) {
            return $this->error($result, $result['data']["errmsg"]);
        }
        return $result;
    }

    /**
     * 查询指定发布审核单的审核状态
     * 提交审核后，调用本接口可以查询指定发布审核单的审核状态
     * @param array $param
     */
    public function getAuditstatus($param = []){
        $url = "https://api.weixin.qq.com/wxa/get_auditstatus?access_token=%s";
        $data = array(
            'auditid' => $param['auditid'] ?? ''
        );
        $result = $this->weappRequest($url, $data);
        if($result['code'] < 0)
            return $result;

        if (isset($result['data']['errcode']) && $result['data']['errcode'] != 0) {
            return $this->error($result, $result['data']["errmsg"]);
        }
        return $result;
    }

    /**
     * 审核撤回
     * 调用本接口可以撤回当前的代码审核单
     * 注意： 单个帐号每天审核撤回次数最多不超过 1 次，一个月不超过 10 次。
     * @param array $param
     */
    public function undocodeaudit($param = []){
        $url = "https://api.weixin.qq.com/wxa/undocodeaudit?access_token=%s";
        $data = array(
        );
        $result = $this->weappRequest($url, $data,false,'get');
        if($result['code'] < 0)
            return $result;

        if (isset($result['data']['errcode']) && $result['data']['errcode'] != 0) {
            return $this->error($result, $result['data']["errmsg"]);
        }
        return $result;
    }

    /**
     * 发布已通过审核的小程序
     * 调用本接口可以发布最后一个审核通过的小程序代码版本
     * @return array|mixed
     */
    public function release($param = []){
        $url = "https://api.weixin.qq.com/wxa/release?access_token=%s";
        $data = (object)array();
        $result = $this->weappRequest($url, $data);
        if($result['code'] < 0)
            return $result;

        if (isset($result['data']['errcode']) && $result['data']['errcode'] != 0) {
            return $this->error($result, $result['data']["errmsg"]);
        }
        return $result;

    }

    /**
     * 版本回退
     * 调用本接口可以将小程序的线上版本进行回退
     * 注意：
     * 如果没有上一个线上版本，将无法回退
     * 只能向上回退一个版本，即当前版本回退后，不能再调用版本回退接口
     * @param array $param
     */
    public function revertcoderelease($param = []){
        $url = "https://api.weixin.qq.com/wxa/revertcoderelease?access_token=%s";
        $data = array(
        );
        $result = $this->weappRequest($url, $data, '', 'get');
        if($result['code'] < 0)
            return $result;

        if (isset($result['data']['errcode']) && $result['data']['errcode'] != 0) {
            return $this->error($result, $result['data']["errmsg"]);
        }
        return $result;

    }

    /**
     * 设置服务器域名
     * 授权给第三方的小程序，其服务器域名只可以为第三方平台的服务器，当小程序通过第三方平台发布代码上线后，小程序原先自己配置的服务器域名将被删除，只保留第三方平台的域名，所以第三方平台在代替小程序发布代码之前，需要调用接口为小程序添加第三方平台自身的域名。
     * 注意： 需要先将域名登记到第三方平台的小程序服务器域名中，才可以调用接口进行配置。
     * @param array $param
     */
    public function modifyDomain($param = [])
    {
        $url = "https://api.weixin.qq.com/wxa/modify_domain?access_token=%s";
        $data = array(
            'action' => $param['action']//可选值	说明  add	添加    delete	删除 set	覆盖 get	获取
        );
        if ($param['action'] != 'get') {
            $data['requestdomain'] = $param['requestdomain'];
            $data['wsrequestdomain'] = $param['wsrequestdomain'];
            $data['uploaddomain'] = $param['uploaddomain'];
            $data['downloaddomain'] = $param['downloaddomain'];
        }
        $result = $this->weappRequest($url, $data);
        if($result['code'] < 0)
            return $result;

        if (isset($result['data']['errcode']) && $result['data']['errcode'] != 0) {
            return $this->error($result, $result['data']["errmsg"]);
        }
        return $result;
    }

     /*
     * 添加体验者
     * @param array $param
     * @return array|mixed
     */
    public function bindTester($param = []){
        $url = "https://api.weixin.qq.com/wxa/bind_tester?access_token=%s";
        $data = [
            'wechatid' => $param['wechatid']
        ];
        $result = $this->weappRequest($url, $data);
        if($result['code'] < 0)
            return $result;

        if (isset($result['data']['errcode']) && $result['data']['errcode'] != 0) {
            return $this->error($result, $result['data']["errmsg"]);
        }
        return $result;
    }

    /**
     * 解绑体验者
     * @param array $param
     * @return array|mixed
     */
    public function unbindTester($param = []){
        $url = "https://api.weixin.qq.com/wxa/unbind_tester?access_token=%s";
        $data = [
            'userstr' => $param['userstr']
        ];
        $result = $this->weappRequest($url, $data);
        if($result['code'] < 0)
            return $result;

        if (isset($result['data']['errcode']) && $result['data']['errcode'] != 0) {
            return $this->error($result, $result['data']["errmsg"]);
        }
        return $result;
    }

}